{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "692919f7-7e12-4dff-8d94-3fe18a19b7c5",
   "metadata": {},
   "outputs": [],
   "source": [
    "# File: needle.ipynb\n",
    "# Code: Claude Code and Codex\n",
    "# Review: Ryoichi Ando (ryoichi.ando@zozo.com)\n",
    "# License: Apache v2.0"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "63f1d19d-48ee-499a-b2c3-dea19dbae0c3",
   "metadata": {},
   "outputs": [],
   "source": [
    "import random\n",
    "\n",
    "from frontend import App\n",
    "\n",
    "# create an app\n",
    "app = App.create(\"needle\")\n",
    "\n",
    "# create a square sheet mesh\n",
    "V, F = app.mesh.square(res=129)\n",
    "app.asset.add.tri(\"sheet\", V, F)\n",
    "\n",
    "# create a cone mesh to act as the needle\n",
    "V, F = app.mesh.cone(height=5, Nr=40, Ny=64, Nb=12, radius=1, sharpen=2)\n",
    "app.asset.add.tri(\"cone\", V, F)\n",
    "\n",
    "# create a sphere tetrahedral mesh as the weight\n",
    "V, F, T = app.mesh.icosphere(r=0.25, subdiv_count=4).tetrahedralize()\n",
    "app.asset.add.tet(\"sphere\", V, F, T)\n",
    "\n",
    "# create a scene\n",
    "scene = app.scene.create()\n",
    "\n",
    "# add cone needle pointing upward\n",
    "scene.add(\"cone\").at(0, -0.25, 0).scale(0.2).rotate(90, \"x\").pin()\n",
    "\n",
    "# add heavy sphere that will push sheets down onto needle\n",
    "sphere = scene.add(\"sphere\").at(0, 1, 0)\n",
    "sphere.param.set(\"density\", 1e5)\n",
    "sphere.pin().pull().unpin(1.0)\n",
    "\n",
    "# add multiple sheets stacked with rotation\n",
    "n, space = 5, 0.05\n",
    "for i in range(n):\n",
    "    y = (i + 1) * space\n",
    "    deg = i * 90 / n\n",
    "    obj = scene.add(\"sheet\").direction([1, 0, 0], [0, 1, 0])\n",
    "    obj.at(0, y, 0).rotate(90, \"x\").rotate(deg + 5 * random.random(), \"y\")\n",
    "    obj.param.set(\"strain-limit\", 0.05).set(\"friction\", 0.5)\n",
    "\n",
    "# compile the scene and report stats\n",
    "scene = scene.build().report()\n",
    "\n",
    "# preview the initial scene\n",
    "scene.preview()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "23b3ee6a-b63e-4435-a53f-80af5be235d8",
   "metadata": {},
   "outputs": [],
   "source": [
    "# create a new session with the compiled scene\n",
    "session = app.session.create(scene)\n",
    "\n",
    "# set session parameters\n",
    "session.param.set(\"frames\", 120)\n",
    "\n",
    "# build this session\n",
    "session = session.build()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "32e7419c-d410-4ad8-8c3a-cb2bbd9db907",
   "metadata": {},
   "outputs": [],
   "source": [
    "# start the simulation and live-preview the results\n",
    "session.start().preview()\n",
    "\n",
    "# also show simulation logs in realtime\n",
    "session.stream()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "495397b1-1cb4-45b1-af4d-eb56b44e415b",
   "metadata": {},
   "outputs": [],
   "source": [
    "# create an animation from the simulation results\n",
    "session.animate()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f53f2f7a-f8e3-4e47-b643-9b3b27c551b6",
   "metadata": {},
   "outputs": [],
   "source": [
    "# export the animation to file\n",
    "session.export.animation()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9fee8e48-5783-4e2a-bc49-4069a3a45e80",
   "metadata": {},
   "outputs": [],
   "source": [
    "# this is for CI\n",
    "if app.ci:\n",
    "    assert session.finished()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
